package com.github.codemperor.core.logger.aop;

import com.alibaba.fastjson.JSON;
import com.github.codemperor.core.logger.BaseLogger;
import com.github.codemperor.core.message.dingtalk.robot.RobotMarkdownSend;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.joda.time.DateTime;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 顶级日志切面抽象
 */
public abstract class BaseLoggerAspect {
    /**
     * 环绕aop
     *
     * @param point
     * @return
     * @throws Throwable
     */
    protected abstract Object around(ProceedingJoinPoint point) throws Throwable;

    /**
     * 处理异常
     *
     * @param point
     * @param e
     * @throws Throwable
     */
    protected abstract void afterThrowing(JoinPoint point, Exception e) throws Throwable;

    /**
     * 获取环境，qa、demo或者prod
     *
     * @return
     */
    protected abstract String env();

    /**
     * logger日志输出
     */
    protected abstract BaseLogger logger();

    /**
     * 不需要aop日志的,格式为 类名.方法名
     * 注意，如果出现一样的方法名等，可以写类的全路径
     */
    protected abstract List<String> exclude();

    /**
     * 拼装不需要日志的过滤字符串格式
     *
     * @param className
     * @param methodName
     * @return
     */
    protected String excludePackage(String className, String methodName) {
        return className + "." + methodName;
    }

    /**
     * 提供默认的环绕方法
     *
     * @param point
     * @return
     */
    protected Object normalAround(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        Object result = point.proceed();
        long time = System.currentTimeMillis() - beginTime;
        try {
            normalLogProcess(point, result, null, time);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 提供默认的异常处理方法
     *
     * @param point
     * @param e
     * @throws Throwable
     */
    protected void normalAfterThrowing(JoinPoint point, Exception e) throws Throwable {
        try {
            normalLogProcess(point, null, e, 0);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 提供默认的日志处理方法
     *
     * @param joinPoint
     * @param result
     * @param e
     * @param time
     */
    protected void normalLogProcess(JoinPoint joinPoint, Object result, Exception e, long time) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //请求的 类名、方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();

        //某些不需要切面的方法或者类进行过滤
        //某些接口需要下载excel，且同一个response只能执行一次getOutputStream()，导致跟json的getWriter()冲突，需要过滤这些方法或者类
        String classMethod = excludePackage(className, methodName);
        if (null != exclude()) {
            List<String> notNeedLog = exclude().stream().filter(classMethod::contains).collect(Collectors.toList());
            if (notNeedLog.size() > 0) {
                return;
            }
        }

        //请求的参数
        //过滤掉ServletRequest和ServletResponse
        Object[] args = new Object[joinPoint.getArgs().length];
        for (int i = 0; i < joinPoint.getArgs().length; i++) {
            if (joinPoint.getArgs()[i] instanceof ServletResponse || joinPoint.getArgs()[i] instanceof ServletRequest)
                continue;
            args[i] = joinPoint.getArgs()[i];
        }
        if (e == null) {
            logger().info("[{}|{}] costtime: {}ms, request: {}, response: {}",
                    env(),
                    classMethod,
                    time,
                    JSON.toJSONString(args),
                    JSON.toJSONString(result));
        } else {
            logger().error("[{}|{}] request: {}, exception: {}",
                    env(),
                    classMethod,
                    JSON.toJSONString(args),
                    JSON.toJSONString(e));
            //发送钉钉消息
            if (null == env() || !"prod".equalsIgnoreCase(env())) {
                return;
            }
            String title = env() + "|" + methodName + "| Exception";
            RobotMarkdownSend.build()
                    .title(title)
                    .text("Title: " + title, "Method: " + classMethod, "Request: " + JSON.toJSONString(args), "Exception: " + e.getMessage())
                    .reference("by: " + DateTime.now().toString("yyyy/MM/dd HH:mm:ss"))
                    .send();

        }
    }
}
